<?php
abstract class HandlerBase {
	//References $_REQUEST, consider replacing it with $_REQUEST
	//Not using $_REQUEST directly gives more flexibility
	protected $_req;
	
	//Operation code
	protected $_op;
	
	private $_startTime;
	
	/**
	 * Constructor of HandlerBase
	 *
	 * @param array $req
	 */
	public function __construct($req) {
		$this->_req = $req;
		$this->_op = $req['op'];
	}
	
	/**
	 * Destructor of HandlerBase
	 *
	 */
	public function __destruct() {
		
	}
	
	//wrapps handler() to enable timing
	public function doHandle($op) {
		$this->_startTime = microtime(true);
		echo "//start:$this->_startTime request time:${_SERVER['REQUEST_TIME']}\n";
		$this->handle($op);
	}
	public abstract function handle($op);
		
	/**
	 * Writes response and die
	 *
	 * @param bool $success
	 * @param string $data  JSON encoded data
	 * @param string $msg
	 */
	protected function writeResponse($success, $data='undefined', $msg = NULL, $errCode = NULL)
	{
		$t = microtime(true);
		echo '//duration:'.($t - $this->_startTime);
		if ($data === NULL) $data = 'undefined';
		AjaxService::setHeaders();
		ob_end_flush();
		//$r = '{re:'.($success ? 'true' : 'false').", data:$data, msg:'$msg' . errCode === NULL, err: $errCode}";
		$r = sprintf('{re:%s, data:%s%s%s%s}', $success ? 'true' : 'false', $data, 
					$msg === NULL ? '' : ",msg:'$msg'",
					$errCode === NULL ? '' : ",err:$errCode",
					',duration:'.($t - $this->_startTime));
		echo $r;
		die;
	}
	
	/**
	 * Reponses to an invalid request
	 * e.g. when the incoming parameters are invalid
	 *
	 */
	protected function writeResponseToInvalidRequest($msg='')
	{
		$this->writeResponse(false, 'undefined', 'Invalid Request'.$msg);
		die;
	}
	
	/**
	 * Writes response indicating an error
	 *
	 */
	protected function writeErrorResponse($msg='', $errCode = NULL)
	{
		if ($errCode === NULL) $errCode = '200' ;
		$this->writeResponse(false, 'false', $msg, $errCode);
		die;
	}
	
	/**
	 * Returns an array of which the elements' keys are mapped from elements' keys of another array
	 * The mapping works like a key transformer
	 * @param array $src The source array
	 * @param array $keyMappings e.g. array('oldKey1'=>'newKey1', 'oldKey2'=>'newKey2')
	 */
	protected function arrayElementsMapKeys ($src, $keyMappings) {
		$ret = array();
		foreach ($src as $ele) {
			$a = array();
			foreach ($keyMappings as $k => $v) {
				$a[$v] = $ele[$k];
			}
			$ret[] = $a;
		}
		return $ret;
	}
	
	/**
	 * Returns an array of which the elements are mapped from the source array.
	 * The mapping works like a filter. 
	 * @example 
	 * $src = array('name' => 'Mike', 'age' => 21, 'RP' => 'Infinite');
	 * $mapped = arrayMapKeys($src, array('name' => 'myName', 'PT'));
	 * //$mapped will be array('myName' => 'Mike', 'RP' => 'Infinite');
	 * @param array $src
	 * @param array $keyMappings
	 * @return array
	 */
	protected function arrayMapKeys ($src, $keyMappings) {
		$ret = array();
		foreach ($keyMappings as $k => $v) {
			if (!is_int($k)) {
				$ret[$v] = $src[$k];
			} else {
				$ret[$v] = $src[$v];
			}
		}
		return $ret;
	}
	
	/**
	 * Gets a variable from $_REQUEST
	 *
	 * @param string $paraName
	 * @return string or NULL if not found
	 */
	protected function getParam($paraName)
	{
		if (isset($this->_req[$paraName]) && (true || count($this->_req[$paraName]) > 0)) {
				return $this->_req[$paraName];
		} else {
			return NULL;
		}
	}
	
	protected function assertNotNull()
	{
		$args = func_get_args();
		foreach ($args as $a) {
			if ($a === NULL) {
				$this->writeResponseToInvalidRequest();
				return false;
			}
		}
		return true;
	}
	/**
	 * Gets parameters from $_REQUEST, asserts presence and returns an associative array.
	 * Used together with extract()
	 * @example
	 * // Declare your variables by assigning NULL, this is optional and is to hint your IDE
	 * $param1 = NULL;
	 * $p = $this->prepareParams('param1'); //variable names, using PHP's variable-length function argument lists  
	 * if ($p === false) die; //false if any parameter not found
	 * extract($p, EXTR_IF_EXISTS); 
	 * @return array | false
	 */
	protected function prepareParams()
	{
		$params = array();
		$args = func_get_args();
		foreach ($args as $a) {
			$v = $this->getParam($a);
			if ($v === NULL) {
				//$this->writeResponseToInvalidRequest();
				return false;
			}
			$params[$a] = $v;
		}
		return $params;
	}
	
	/**
	 * Prepares incomping http request parameters and asserts their presence.
	 * If (any parameter omitted) die;
	 * @param ...(variable length) string parameter name
	 *
	 * @return array associative array, param name as key, param value as value
	 */
	protected function requireParams()
	{
		$args = func_get_args();
		$p = call_user_func_array(array(&$this, 'prepareParams'), $args);
		if ($p === false) //there are invalid params
		{
			$this->writeResponseToInvalidRequest('You want to hack?');
			die;
		} else {
			return $p;
		}
	}
	
	protected function requireCurrentUser($key=NULL)
	{
		$d = Auth::GetData($key);
		if($d === false) $this->writeErrorResponse('请先登录');
		else return $d;
	}
	
	/**
	 * Returns a string recognizable by JavaScript new Date(str)
	 * RFC2822 format is currently used
	 *
	 * @param string|int  $date if NULL, current time is used; if int, it'll be treated as timestamp
	 * @return string
	 */
	protected function jsDate($date=NULL)
	{
		if (is_string($date)) 
			$t = strtotime($date);
		else if(is_int($date)) 
			$t = $date;
		else 
			$t = time();
			
		if ($t === false) 
			return false;
		else 
			return date(DATE_RFC2822, $t);
	}
	
	/**
	 * Formats a date for MySQL queries
	 *
	 * @param mixed $date string|timestamp|NULL
	 * @return string
	 */
	protected function mySqlDate($date=NULL)
	{
		return DbMySQL::mySqlDateTime($date);
	}
	
	protected function jsonEncode($var) {
		return json_encode($var);
	}
	protected function jsonDecode($str){
		return json_decode($str);
	}
}

?>